#!/bin/bash
# 容器内执行该脚本作为入口, 在依赖的服务都启动之后才启动程序
# Usage: ./entrypoint.sh -h <依赖服务IP地址> -p <依赖服务端口号, 多个端口号用逗号分隔> -c <要执行的命令(若带选项，需要使用引号)>
# 若指定端口程序没有启动则阻塞等待, 使用nc命令判断
function wait_for() {
    local ip="${1}"
    local port="${2}"
    local timeout="${3:-30}"
    local interval="${4:-1}"
    # $()用于命令替换, 将命令的输出结果作为字符串赋值给变量
    local start_time="$(date +%s)"
    # (()) 中的变量不需要加$
    # (()) 用于算术运算, 可以进行比较运算
    # (()) 还可用于for循环 ((i=0; i<10; i++))
    local end_time="$((start_time + timeout))"
    while ! nc -zv ${ip} ${port} &> /dev/null
    do
        echo "等待${ip}:${port}启动"
        sleep ${interval}
        if test "$(date +%s)" -ge "${end_time}"
        then
            echo "等待${ip}:${port}超时"
            return 1
        fi
    done
}

# 提取命令行参数 -h 后面的值作为依赖服务的IP地址, -p 后面的值作为依赖服务的端口号
ip=""
ports=()
command=""
while getopts "h:p:c:" OPT
    do
        case ${OPT} in
            h)
                ip="${OPTARG}"
                ;;
            p)
                # IFS是internal field separator的缩写, 用于指定分隔符, 会作用于read命令
                # 如果后面还有使用read但是不以逗号分隔的情况, 需要在使用完后恢复IFS
                # -a 选项表示将读取的值存储到数组中
                # -r 选项表示不允许反斜杠转义
                # <<< 表示将右侧的字符串作为输入传递给read命令
                # 如果不使用<<<, 则需要使用管道符号|将字符串传递给read命令
                IFS=',';read -ra ports<<<"${OPTARG}"
                ;;
            c)
                command="${OPTARG}"
                ;;
        esac
    done
# echo "${command}"
# 等待依赖服务启动
# ports[@]与ports[*]不同，前者表示数组的所有元素, 后者表示将数组的所有元素作为一个整体
# ${#ports[@]}表示数组的长度
for port in "${ports[@]}"
do
    if wait_for "${ip}" "${port}"
    then
        echo "${ip}:${port} 已启动"
    else
        echo "等待 ${ip}:${port} 启动失败, 退出"
        exit 1
    fi 
done
# eval "cat ./conf/file.conf"
# 执行入口程序
exec sh -c "${command}"